// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#ifndef LLVM_JIT_EXECUTOR
#define LLVM_JIT_EXECUTOR

#include <llvm/Module.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>

#include <set>
#include <map>
#include <string>

#include "Coroutine.h"
#include "JitBindings.h"
#include "JitRecompiler.h"

#include "../PowerPC.h"

namespace LLVMJit
{

class CoroutineCall;

// The JitExecutor class is responsible for executing the LLVM code generated
//	by the JitRecompiler, and provides all the runtime facilities required by
//	the generated LLVM bytecode to work.

// It also keeps track of the execution, and knows where and how to resume
//	execution the best. It also handles PPC exceptions.

// When the JIT meets an indirect call or indirect jump (which are quite
//	frequent when disassembling code of an object-oriented language such as
//	C++), it should call down to RunStub in order to dispatch the call
//	appropriately. In the case of an indirect jump, to avoid dying from a
//	stack overflow, we can abuse the tail call property of not allocating a new
//	stack frame to get away with it.

class JitExecutor
{
public:
	static const u32 eventLoopCycleTreshold;
	
private:
	typedef u32 (*RecompiledFunction)
		(JitExecutor*, Coroutine*, PowerPC::PowerPCState*);
	
	u32 expectedNPC;
	u32 elapsedCycles;
	llvm::ExecutionEngine* engine;
	JitRecompiler* recompiler;
	JitBindings* bindings;
	std::map<llvm::Function*, RecompiledFunction> compiledCache;
	
	Coroutine* coroutine;
	CoroutineCall* starter;
	
	static void CoroutineStarter(Coroutine*, JitExecutor*);
	
	RecompiledFunction Compile(llvm::Function* function);
	RecompiledFunction GetCompiled(llvm::Function* function);
	
public:
	JitExecutor(llvm::ExecutionEngine* engine, JitRecompiler* recompiler);
	~JitExecutor();
	
	JitBindings* GetBindings();
	void Resume();
	void ClearCache();
	void ClearCache(u32 address);
	
	// Runtime services
	u32 RunStub(Coroutine* coroutine, u32 addr);
	void SetExpectedNPC(u32 expectedNPC);
	void Break(Coroutine* coroutine, u32 expectedNPC);
	
	u32 GetCyclesCount();
	void ResetCyclesCount();
};

}

#endif